﻿using System;
using System.Diagnostics;
using System.Threading;
using JetBlack.Diagnostics;

namespace JetBlack.Services
{
    /// <summary>
    /// A class which manages the transition bewteen server states.
    /// </summary>
    abstract public class StateManagedServer : IManageableServer
    {
        private enum StateType { Stopping, Stopped, Started, Paused, Starting }

        private AutoResetEvent stateChangeEvent = new AutoResetEvent(false);
        private StateType stateChangeType = StateType.Starting;

        #region IManageableServer Members

        /// <summary>
        /// Start the server.
        /// </summary>
        /// <param name="args">The startup parameters.</param>
        public void Start(string[] args)
        {
            TraceSource.Information("Starting Server");

            lock (this)
            {
                stateChangeType = StateType.Starting;
            }

            if (!OnStart(args))
            {
                lock (this)
                    stateChangeType = StateType.Stopped;
                TraceSource.Critical("Failed to start server");
                return;
            }

            lock (this)
            {
                if (stateChangeType == StateType.Starting)
                    stateChangeType = StateType.Started;
            }

            try
            {
                if (stateChangeType == StateType.Started)
                {
                    while (true)
                    {
                        // Check to see if we've been asked to pause
                        bool has_changed = stateChangeEvent.WaitOne();
                        if (has_changed)
                        {
                            lock (this)
                            {
                                if (stateChangeType == StateType.Paused)
                                {
                                    if (!OnPause())
                                    {
                                        TraceSource.Error("Failed to pause server");
                                        stateChangeType = StateType.Started;
                                    }

                                    continue;   // Wait until the state changes again
                                }
                                else if (stateChangeType == StateType.Stopping)
                                {
                                    break;      // Quit the poll loop
                                }
                            }
                        }

                        lock (this)
                        {
                            if (stateChangeType == StateType.Started)
                            {
                                if (!OnResume())
                                {
                                    TraceSource.Error("Failed to resume server - stopping.");
                                    Stop();
                                }
                            }
                        }
                    }
                }

                OnStop();
            }
            catch (Exception ex)
            {
                TraceSource.Error(ex);
            }

            lock (this)
            {
                stateChangeType = StateType.Stopped;
            }
        }

        /// <summary>
        /// Stop the server.
        /// </summary>
        public void Stop()
        {
            TraceSource.Information("Stopping Server");

            lock (this)
            {
                if (stateChangeType == StateType.Starting || stateChangeType == StateType.Started || stateChangeType == StateType.Paused)
                {
                    stateChangeType = StateType.Stopping;
                    stateChangeEvent.Set();
                }
            }
        }

        /// <summary>
        /// Pause the server.
        /// </summary>
        public void Pause()
        {
            TraceSource.Information("Pausing Server");

            lock (this)
            {
                if (stateChangeType != StateType.Started)
                {
                    TraceSource.Warning("Unable to pause as the server is not in a started state. The server is currently in the state \"{0}\"", stateChangeType);
                }
                else
                {
                    stateChangeType = StateType.Paused;
                    stateChangeEvent.Set();
                }
            }
        }

        /// <summary>
        /// Resume the server.
        /// </summary>
        public void Resume()
        {
            TraceSource.Information("Resuming Server");

            lock (this)
            {
                if (stateChangeType != StateType.Paused)
                {
                    TraceSource.Warning("Unable to resume as the server is not in a paused state. The server is currently in the state \"{0}\"", stateChangeType);
                }
                else
                {
                    stateChangeType = StateType.Started;
                    stateChangeEvent.Set();
                }
            }
        }

        #endregion

        /// <summary>
        /// The method to be run when the server transitions from to the start state to the running state.
        /// </summary>
        /// <param name="args">The startup parameters.</param>
        /// <returns>When successful returns true.</returns>
        protected abstract bool OnStart(string[] args);

        /// <summary>
        /// The method to be run when the server transitions from the running state to the pause state.
        /// </summary>
        /// <returns>When successful returns true.</returns>
        protected abstract bool OnPause();

        /// <summary>
        /// The method to be run when the server transitions from the pause state to the running state.
        /// </summary>
        /// <returns>When successful returns true.</returns>
        protected abstract bool OnResume();

        /// <summary>
        /// The method to be run when the server transitions from the running state to the stopped state.
        /// </summary>
        /// <returns>When successful returns true.</returns>
        protected abstract void OnStop();

        /// <summary>
        /// The logger.
        /// </summary>
        abstract public TraceSource TraceSource { get; }
    }
}
